﻿#if UNITY_EDITOR

using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine;
using UnityEditor;

namespace HEFramework
{
    /// <summary>
    ///
    /// AB打包工具
    /// 
    /// ----------------------------------------
    /// 作者 hep
    /// 创建时间：2023-12-21 17:43:56
    /// ----------------------------------------
    /// </summary>
    public class AssetBundleBuildEditor : AssetPostprocessor
    {
        private static AssetBundleSettingScriptableObject data = AssetBundleSettingScriptableObject.Instance;

        /// <summary>
        /// AB包资源生成入口
        /// </summary>
        /// <param name="_target">目标平台</param>
        public static void AssetBundlesBuild(BuildTarget _target)
        {
            string t = data.AssetBundleDataBuildTargetPath;

            //最终目录
            t = t + "/" + _target.ToString();
            t = t.RB();

            //临时目录
            string o = t + "/Original/" + data.AssetBundleCentralFileName;
            o = o.RB();


            //清除旧数据
            System.IO.DirectoryInfo di;
            if (System.IO.Directory.Exists(t))
            {
                di = new System.IO.DirectoryInfo(t);
                di.Delete(true);
            }

            if (System.IO.Directory.Exists(o))
            {
                di = new System.IO.DirectoryInfo(o);
                di.Delete(true);
            }

            //在指定路径中创建所有目录和子目录
            System.IO.Directory.CreateDirectory(t);
            System.IO.Directory.CreateDirectory(o);

            //AB包资源初始化设置
            SetAssetBundleInit();

            //压缩模式
            BuildAssetBundleOptions options = data.Compress;

            //AssetBundle资源压缩导出到备用文件夹下
            AssetBundleManifest manifest = BuildPipeline.BuildAssetBundles(o, options, _target);

            Refresh();

            //复制
            Copy(o, t);

            //删除未加密的文件
            di = new System.IO.DirectoryInfo(t + "/" + "Original");
            di.Delete(true);

            //资源文件生成完毕
            Debug.Log("===== Asset building ready =====");

            List<string> builtAssetBundlePaths = new List<string>();

            //所有AB包的路径
            builtAssetBundlePaths.AddRange(manifest.GetAllAssetBundles());

            //验证文件
            CreateCheckFile(builtAssetBundlePaths.ToArray(), t);

            //列表文件
            CreateDownloadCsv(builtAssetBundlePaths.ToArray(), t);

            //所有东西生成完毕
            Debug.Log("===== Finish building assetBundles =====");

            //打开窗口
            UnityEditor.EditorUtility.RevealInFinder(t);
        }


        /// <summary>
        /// AB包路径清空
        /// </summary>
        public static void ClearAssetBundlePath()
        {
            var abs = AssetDatabase.GetAllAssetBundleNames();

            for (int i = 0; i < abs.Length; i++)
            {
                AssetDatabase.RemoveAssetBundleName(abs[i], true);
            }

            Refresh();
            Debug.Log("AB包路径清空完成");
        }


        /// <summary>
        /// AB包打包初始化
        /// </summary>
        private static void SetAssetBundleInit()
        {
            //最终资源路径列表
            List<string> assetBundlePaths = new List<string>();


            //筛选出需要打包的文件夹
            List<string> needPaths = new List<string>();


            string[] paths = System.IO.Directory.GetDirectories(data.AssetBundleDataOriginalPath, "*", System.IO.SearchOption.AllDirectories);

            for (int i = 0; i < paths.Length; i++)
            {
                paths[i] = paths[i].RB();
            }

            needPaths.AddRange(paths);


            //AB打包规则：当前文件夹下无子文件夹，同时拥有的文件数量大于0，则视为可以打成AB包的合法文件夹
            //查询到最终资源路径
            foreach (string path in needPaths)
            {
                //下一层路径
                string[] directories = System.IO.Directory.GetDirectories(path);

                //如果还有下一层目录则无视（当前不是最终一层目录）
                if (directories.Length > 0)
                {
                    continue;
                }

                //获取最终一层目录下所有的文件资源
                string[] files = System.IO.Directory.GetFiles(path);


                //防空处理
                if (files.Length <= 0)
                {
                    continue;
                }

                assetBundlePaths.Add(path);
            }


            //资源是否发生变更标识符
            bool isNeedToSave = false;


            //更新打包资源
            AssetDatabase.Refresh();


            foreach (string path in assetBundlePaths)
            {
                //封装AB包信息 通过指定路径来导入资源
                var importer = AssetImporter.GetAtPath(path);


                if (importer == null)
                {
                    Debug.LogFormat("Importer is null : {0}", path);
                    continue;
                }

                //资源AB包命名
                string bundleName = path.Replace(data.AssetBundleDataOriginalPath + "/", "").ToLower();


                //AB包正确性检测
                EditorUtility.DisplayProgressBar("AssetBundle directory: ", path, 0);
                importer.assetBundleName = bundleName;
                importer.SaveAndReimport();
            }
            
            //保存储存的AB包资源
            Refresh();


            EditorUtility.ClearProgressBar();
        }


        /// <summary>
        /// 文件复制
        /// </summary>
        /// <param name="_o">原始目录</param>
        /// <param name="_t">输出目录</param>
        private static void Copy(string _o, string _t)
        {
            //获取_o下的所有文件（不包含目录）
            string[] filePaths = System.IO.Directory.GetFiles(_o, "*", System.IO.SearchOption.AllDirectories);

            //格式化路径
            for (int i = 0; i < filePaths.Length; i++)
            {
                filePaths[i] = filePaths[i].RB();
            }

            Aes aes = new Aes(data.Key, data.IV);
            foreach (string path in filePaths)
            {
                //.manifest文件剔除
                if (path.Contains(".manifest"))
                {
                    continue;
                }

                byte[] bytes = System.IO.File.ReadAllBytes(path);
                string abName = path.Replace(_o + "/", "");
                string t = _t + "/" + abName;
                //如果选择了加密
                if (data.Encrypt)
                {
                    //内容加密
                    bytes = aes.EncryptSyn(bytes);
                    //文件名加密
                    abName = MD5Hash.ComputeHashToHex(abName);
                    t = _t + "/" + abName;
                }

                //在指定路径中创建所有目录和子目录，除非它们已经存在。
                System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(t + ".txt"));

                //将二进制流写入
                System.IO.File.WriteAllBytes(t, bytes);
            }
        }


        /// <summary>
        /// 验证文件.check的生成
        /// </summary>
        /// <param name="_abs">所有AB包路径</param>
        /// <param name="_t">文件输出的路径</param>
        private static void CreateCheckFile(string[] _abs, string _t)
        {
            if (_abs.Length <= 0)
            {
                return;
            }

            // 路径取得
            string[] abs = _abs;
            FileCheckList checkList = new FileCheckList();

            //路径
            string path = null;
            //字节
            byte[] bytes = null;
            //Hash值
            string hash = null;
            //根据AB路径数量创建信息体 
            checkList.CreateData(abs.Length);
            for (int i = 0; i < abs.Length; i++)
            {
                path = abs[i];
                if (data.Encrypt)
                {
                    path = MD5Hash.ComputeHashToHex(path);
                }

                bytes = System.IO.File.ReadAllBytes(_t + "/" + path);
                hash = MD5Hash.ComputeHashToHex(bytes);
                FileCheckList.FileCheckData checkData = new FileCheckList.FileCheckData(path, hash, bytes.Length);
                checkList.AddData(checkData);
            }

            //加入中心文件
            path = data.AssetBundleCentralFileName;
            if (data.Encrypt)
            {
                path = MD5Hash.ComputeHashToHex(path);
            }

            bytes = System.IO.File.ReadAllBytes(_t + "/" + path);
            hash = MD5Hash.ComputeHashToHex(bytes);
            checkList.AddData(new FileCheckList.FileCheckData(path, hash, bytes.Length));


            checkList.Version = 1;

            //Json的形式保存
            string json = JsonConvert.SerializeObject(checkList);
            byte[] datas = System.Text.Encoding.UTF8.GetBytes(json);
            string fileName = checkList.FileName;
            if (data.Encrypt)
            {
                Aes aes = new Aes(data.Key, data.IV);
                fileName = MD5Hash.ComputeHashToHex(fileName);
                //加密字节流
                datas = aes.EncryptSyn(datas);
            }


            //文件流写入文件
            System.IO.File.WriteAllBytes(_t + "/" + fileName, datas);
        }


        /// <summary>
        /// 资源列表文件.csv导出
        /// </summary>
        /// <param name="_abs"></param>
        /// <param name="_t"></param>    
        private static void CreateDownloadCsv(string[] _abs, string _t)
        {
            if (_abs.Length <= 0)
            {
                return;
            }

            FileDownloadCsv downloadCsv = null;

            downloadCsv = new FileDownloadCsv();
            downloadCsv.Datas = new List<FileDownloadCsv.FileDownloadCsvData>();

            string[] abs = _abs;

            for (int i = 0; i < abs.Length; ++i)
            {
                //是否有不同路径的资源文件
                if (!downloadCsv.Datas.Exists(d => d.ABName0 == abs[i]))
                {
                    downloadCsv.Datas.Add(new FileDownloadCsv.FileDownloadCsvData()
                    {
                        ABName0 = abs[i],
                        ABName1 = MD5Hash.ComputeHashToHex(abs[i]),
                    });
                }
            }

            //中心文件
            downloadCsv.Datas.Add(new FileDownloadCsv.FileDownloadCsvData()
            {
                ABName0 = data.AssetBundleCentralFileName,
                ABName1 = MD5Hash.ComputeHashToHex(data.AssetBundleCentralFileName),
            });

            //验证文件
            downloadCsv.Datas.Add(new FileDownloadCsv.FileDownloadCsvData()
            {
                ABName0 = data.AssetBundleCheckFileName,
                ABName1 = MD5Hash.ComputeHashToHex(data.AssetBundleCheckFileName),
            });


            // Csv导出
            downloadCsv.Export(_t);
        }


        /// <summary>
        /// 刷新
        /// </summary>
        private static void Refresh()
        {
            AssetDatabase.RemoveUnusedAssetBundleNames();
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate | ImportAssetOptions.ImportRecursive);
        }
    }
}
#endif